package com.github.lh.config;

import com.github.lh.support.Table;
import com.github.lh.support.TableScanner;
import lombok.Getter;
import org.apache.velocity.VelocityContext;
import org.jetbrains.annotations.NotNull;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static com.github.lh.support.StringUtils.concatPath;
import static com.github.lh.support.StringUtils.packageToPath;

/**
 * @author <a href="mailto: 393803588@qq.com">刘涵(Hanl)</a>
 * By 2017/12/6
 */
public class Configuration {

    @Getter
    private Map<String, VelocityContext> properties;
    /**
     * 全局配置
     */
    @Getter
    private GlobalConfig globalConfig;
    /**
     * 数据源配置
     */
    private TableScanner tableScanner;
    /**
     * 包配置
     */
    private PackageConfig packageInfo;
    /**
     * 模版配置
     */
    @Getter
    private TemplateConfig templateConfig;

    private Configuration(GlobalConfig globalConfig, TableScanner tableScanner,
                          PackageConfig packageConfig, TemplateConfig templateConfig) {

        this.properties = new HashMap<>();
        this.globalConfig = globalConfig;
        this.tableScanner = tableScanner;
        this.packageInfo = packageConfig;
        this.templateConfig = templateConfig;
        init();
    }

    public static ConfigurationBuilder builder() {
        return new ConfigurationBuilder();
    }

    private void init() {
        if (this.tableScanner == null) {
            throw new IllegalArgumentException("miss table scanner");
        }

        LocalDateTime now = LocalDateTime.now();
        for (Table table : this.tableScanner.getTables()) {
            String entityName = table.getClassName();
            VelocityContext ctx = new VelocityContext();
            ctx.put("entityName", entityName);
            ctx.put("tableName", table.getName());
            ctx.put("entityFieldName", table.getFieldName());
            ctx.put("primaryTypeName", table.getPrimary().getDataType().getSimpleName());
            ctx.put("primaryTypeClass", table.getPrimary().getDataType().getTypeName());
            ctx.put("author", globalConfig.getAuthor());
            ctx.put("email", globalConfig.getEmail());
            ctx.put("isRestController", globalConfig.isRestController());
            ctx.put("date", now);
            ctx.put("entityImports", table.getImports());
            ctx.put("columns", table.getColumns());
            initPackage(entityName, ctx);

            this.properties.put(entityName, ctx);
        }
    }

    private void initPackage(String entityName, VelocityContext ctx) {
        String outputDir = globalConfig.getOutputDir();

        String entityPackage = packageInfo.getEntity();
        ctx.put("entityPackage", entityPackage);
        ctx.put("entityClassName", entityName);
        ctx.put("entityClassFullName", concatClassName(entityPackage, entityName));
        ctx.put("entityClassFile", getPackagePath(entityName, outputDir, entityPackage));

        if (globalConfig.isGenDao()) {
            String daoPackage = packageInfo.getDao();
            String daoClassName = globalConfig.getDaoName(entityName);
            ctx.put("daoPackage", daoPackage);
            ctx.put("daoClassName", daoClassName);
            ctx.put("daoClassFullName", concatClassName(daoPackage, daoClassName));
            ctx.put("daoClassFile", getPackagePath(daoClassName, outputDir, daoPackage));
        }

        if (globalConfig.isGenService()) {
            String servicePackage = packageInfo.getService();
            String serviceClassName = globalConfig.getServiceName(entityName);
            ctx.put("servicePackage", servicePackage);
            ctx.put("serviceClassName", serviceClassName);
            ctx.put("serviceClassFullName", concatClassName(servicePackage, serviceClassName));
            ctx.put("serviceClassFile", getPackagePath(serviceClassName, outputDir, servicePackage));

            String serviceImplPackage = packageInfo.getServiceImpl();
            String serviceImplClassName = globalConfig.getServiceImplName(entityName);
            ctx.put("serviceImplPackage", serviceImplPackage);
            ctx.put("serviceImplClassName", serviceImplClassName);
            ctx.put("serviceImplClassFullName", concatClassName(serviceImplPackage, serviceImplClassName));
            ctx.put("serviceImplClassFile", getPackagePath(serviceImplClassName, outputDir, serviceImplPackage));
        }

        if (globalConfig.isGenController()) {
            String controllerPackage = packageInfo.getController();
            String controllerClassName = globalConfig.getControllerName(entityName);
            ctx.put("controllerPackage", controllerPackage);
            ctx.put("controllerClassName", controllerClassName);
            ctx.put("controllerClassFullName", concatClassName(controllerPackage, controllerClassName));
            ctx.put("controllerClassFile", getPackagePath(controllerClassName, outputDir, controllerPackage));
        }
    }

    private String concatClassName(String entityPackage, String entityName) {
        return entityPackage.concat(".").concat(entityName);
    }

    @NotNull
    private String getPackagePath(String entityName, String outputDir, String entityPackage) {
        return concatPath(outputDir, packageToPath(entityPackage), entityName).concat(".java");
    }

    public static class ConfigurationBuilder {
        private GlobalConfig globalConfig;
        private TableScanner tableScanner;
        private PackageConfig packageInfo;
        private TemplateConfig templateConfig;

        public Configuration build() {
            return new Configuration(
                    Optional.ofNullable(globalConfig).orElse(new GlobalConfig()),
                    this.tableScanner,
                    Optional.ofNullable(packageInfo).orElse(new PackageConfig()),
                    Optional.ofNullable(templateConfig).orElse(new TemplateConfig())
            );
        }

        public ConfigurationBuilder globalConfig(GlobalConfig globalConfig) {
            this.globalConfig = globalConfig;
            return this;
        }

        public ConfigurationBuilder tableScanner(TableScanner tableScanner) {
            this.tableScanner = tableScanner;
            return this;
        }

        public ConfigurationBuilder packageConfig(PackageConfig packageConfig) {
            this.packageInfo = packageConfig;
            return this;
        }

        public ConfigurationBuilder templateConfig(TemplateConfig templateConfig) {
            this.templateConfig = templateConfig;
            return this;
        }
    }
}
